﻿using Digitalmes.Application.Utils;

namespace Digitalmes.Application.Mes;

/// <summary>
/// 工序 BOM 校验应用服务。
/// </summary>
public sealed class ProcBomCheckService : IApplicationService
{
    private readonly SugarRepository<ProcBom> _bomRepo;
    private readonly SugarRepository<ProdMaterialConsume> _materialConsumeRepo;

    public ProcBomCheckService(SugarRepository<ProcBom> bomRepo, 
        SugarRepository<ProdMaterialConsume> materialConsumeRepo)
    {
        _bomRepo = bomRepo;
        _materialConsumeRepo = materialConsumeRepo;
    }

    /// <summary>
    /// 校验上料条码规则，若匹配，则获取此 BOM 项。
    /// </summary>
    /// <param name="barcode">上料条码</param>
    /// <param name="step">上料步序</param>
    /// <param name="transSn">SN 信息</param>
    /// <param name="routingId">工艺路线 Id</param>
    /// <param name="processId">工序 Id</param>
    /// <returns></returns>
    public async Task<(bool, ProcBom?)> CheckAndGetFeedingAsync(string barcode, int step, ProdTransSn transSn, long routingId, long processId)
    {
        // 找到该关键物料对应的 BOM，并校验条码规则
        var bomMaterials = await _bomRepo.AsQueryable()
                .Includes(s => s.Material)
                .Where(s => s.ProductId == transSn.ProductId
                    && s.RoutingId == routingId
                    && s.ProcessId == processId)
                .OrderByDescending(s => s.Pos)
                .ToListAsync();

        // step 步序大于 0，按步序来获取对应的物料信息（精准匹配）
        if (step > 0)
        {
            var bomMaterial = bomMaterials.FirstOrDefault(s => s.Pos == step);
            if (bomMaterial == null)
            {
                return (false, null);
            }

            // 校验物料条码规则是否满足
            var ok = RuleCheckerUtil.CheckRules(barcode, bomMaterial.Material!.Rules, AppConsts.BarcodeRulePlaceholder, true);
            return (ok, bomMaterial);
        }

        // TODO：此方法只能找到第一个匹配项，若有多项都能匹配，会出现问题。
        var count = await _materialConsumeRepo.CountAsync(s => s.Sn == transSn.Sn && s.OpId == transSn.CurOpId && !s.IsUnbound);
        var bom = bomMaterials
            .Skip(count)
            .FirstOrDefault(s => RuleCheckerUtil.CheckRules(barcode, s.Material!.Rules, AppConsts.BarcodeRulePlaceholder, true));

        return (bom != null, bom);
    }

    /// <summary>
    /// 校验产品过站时所上的料与工艺 BOM 是否匹配。
    /// </summary>
    /// <param name="transSn">SN 信息</param>
    /// <param name="routingId">工艺路线 Id</param>
    /// <param name="processId">工序 Id</param>
    /// <returns>ok=>校验是否成功；todo=>还需待上的物料编号集合</returns>
    public async Task<(bool ok, List<string>? todo)> CheckArchiveAsync(ProdTransSn transSn, long routingId, long processId)
    {
        var bomMaterials = await _bomRepo.GetListAsync(s => s.ProductId == transSn.ProductId
                && s.RoutingId == routingId
                && s.ProcessId == processId);

        if (!bomMaterials.Any())
        {
            return (true, null);
        }

        var materials = await _materialConsumeRepo.GetListAsync(s => s.Sn == transSn.Sn
            && s.LineCode == transSn.CurLineCode
            && s.ProcCode == transSn.CurProcCode
            && s.OpCode == transSn.CurOpCode
            && !s.IsUnbound);

        List<string> todo = new();
        foreach (var m in bomMaterials)
        {
            var count = materials.Count(s => s.MaterialCode == m.MaterialCode);
            if (count != m.Qty)
            {
                todo.Add(m.MaterialCode);
            }
        }

        return (!(todo.Count > 0), todo);
    }
}
